package com.hongtech.tiny.security.util;

import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.*;
import java.util.function.BinaryOperator;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.ToLongFunction;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import static java.util.Comparator.comparingLong;
import static java.util.stream.Collectors.collectingAndThen;
import static java.util.stream.Collectors.toCollection;

public class StreamUtils {

    private static final Logger _log = LoggerFactory.getLogger(StreamUtils.class);

    private static <T, R> Stream<R> getStream(Collection<T> data, Predicate<T> predicate, Comparator<T> comparator, Function<T, R> function) {
        if (CollectionUtils.isEmpty(data)) {
            throw new IllegalArgumentException();
        }
        Stream<T> stream = data.stream();
        if (predicate != null) {
            stream = stream.filter(predicate);
        }
        if (comparator != null) {
            stream = stream.sorted(comparator);
        }
        return stream.map(function);
    }

    /**
     * 映射某个属性转换 List
     */
    public static <T, R> List<R> toList(List<T> data, Function<T, R> function) {
        if (CollectionUtils.isEmpty(data)) {
            return new ArrayList<>();
        }
        return getStream(data, null, null, function).collect(Collectors.toList());
    }

    /**
     * 过滤后映射某个属性转换 List
     */
    public static <T, R> List<R> toList(List<T> data, Predicate<T> predicate, Function<T, R> function) {
        if (CollectionUtils.isEmpty(data)) {
            return new ArrayList<>();
        }
        return getStream(data, predicate, null, function).collect(Collectors.toList());
    }

    /**
     * 排序后映射某个属性转换 List
     * <p>
     * 当 Comparator 序属性值有nuLL时
     * 1.必须先把null置为 nullsFirst 或 nullsLast 否则会报空指针: Comparator,comparing(T::排序属性, Comparator.nullsLast(排序属性类型::compare))
     * 或 Comparator.comparing(T::排序属性，Comparator.nulLsFirst(排序属性类型::compare))
     * 2.倒序时，如果依旧想把 null 放在最后，需要先把 null 置为 nullsFirst: Comparator.comparing(T::排序属性，Comparator.nullsFirst(排序属性类型:compare)).reversed()
     */
    public static <T, R> List<R> toList(List<T> data, Comparator<T> comparator, Function<T, R> function) {
        if (CollectionUtils.isEmpty(data)) {
            return new ArrayList<>();
        }
        return getStream(data, null, comparator, function).collect(Collectors.toList());
    }

    /**
     * 过滤排序后映射某个属性转换 List
     * <p>
     * 当 Comparator 序属姓值有 null 时
     * 1.必须先把 null 置为 nullsFirst 或 nullsLast 否则会报空指针: Comparator,comparing(T::排序属性, Comparator.nullsLast(排属性类型::compare))
     * 或 Comparator.comparing(T::排序属性，Comparator.nullsFirst(排序属性类型::compare))
     * 2.倒序时，如果依旧想把 null 放在最后，则需要先把 null 为 nullsFirst: Comparator,comparing(T::排序属性，Comparator.nullsFirst(产局性类型:compare).reversed()
     */
    public static <T, R> List<R> toList(List<T> data, Predicate<T> predicate, Comparator<T> comparator, Function<T, R> function) {
        if (CollectionUtils.isEmpty(data)) {
            return new ArrayList<>();
        }
        return getStream(data, predicate, comparator, function).collect(Collectors.toList());
    }

    /**
     * 映射某个属性转换 Set
     */
    public static <T, R> Set<R> toSet(Collection<T> data, Function<T, R> function) {
        if (CollectionUtils.isEmpty(data)) {
            return new HashSet<>();
        }
        return getStream(data, null, null, function).collect(Collectors.toSet());
    }


    /**
     * 过滤后映射某个属性转换 Set
     */
    public static <T, R> Set<R> toSet(List<T> data, Predicate<T> predicate, Function<T, R> function) {
        if (CollectionUtils.isEmpty(data)) {
            return new HashSet<>();
        }
        return getStream(data, predicate, null, function).collect(Collectors.toSet());
    }

    /**
     * 排序后映射某个属性转换 Set
     * <p>
     * 当 Comparator 序属性值有 nuLL 时
     * 1.必须先把 null 置为 nullsFirst 或 nullsLast 否会报空针: Comparator.comparing(T::排序属性, Comparator.nullsLast(排序属性类型::compare))
     * 或 Comparator.comparing(T::排序属性，Comparator.nullsFirst(排序属性类型::compare))
     * 2.倒序时,如果依旧想把 null 放在最后，则需要先把 null 置为 nullsFirst: Comparator.comparing(T::排序属性, Comparator.nullsFirst(字属性类型::compare).reversed()
     */
    public static <T, R> Set<R> toSet(List<T> data, Comparator<T> comparator, Function<T, R> function) {
        if (CollectionUtils.isEmpty(data)) {
            return new HashSet<>();
        }
        return getStream(data, null, comparator, function).collect(Collectors.toSet());
    }

    /**
     * 过滤排序后映射某个属性转换 Set
     * <p>
     * 当 Comparator 排序属性值有 null 时
     * 1.必须先把 null 置为 nullsFirst 或 nullsLast 否则会报空指: Comparator.comparing(T:排序属性, Comparator.nullsLast(排序属性类型:compare)
     * 或 Comparator.comparing(T::排序属性，Comparator.nullsFirst(排序属性类型::compare))
     * 2.倒字时,如果依旧想把 null 放在最后，则需先把 null 为 nullsFirst: Comparator.comparing(T::排序属性，Comparator.nullsFirst(排属性类型:compare).reversed()
     */
    public static <T, R> Set<R> toSet(List<T> data, Predicate<T> predicate, Comparator<T> comparator, Function<T, R> function) {
        if (CollectionUtils.isEmpty(data)) {
            return new HashSet<>();
        }
        return getStream(data, predicate, comparator, function).collect(Collectors.toSet());
    }

    /**
     * 映射某个属性转换去重 List
     */
    public static <T, R> List<R> toDistinctList(List<T> data, Function<T, R> function) {
        if (CollectionUtils.isEmpty(data)) {
            return new ArrayList<>();
        }
        return getStream(data, null, null, function).distinct().collect(Collectors.toList());
    }

    /**
     * 过滤后映射某个属性转换去重 List
     */
    public static <T, R> List<R> toDistinctList(List<T> data, Predicate<T> predicate, Function<T, R> function) {
        if (CollectionUtils.isEmpty(data)) {
            return new ArrayList<>();
        }
        return getStream(data, predicate, null, function).distinct().collect(Collectors.toList());
    }

    /**
     * 排序后映射某个属性转换去重 List
     * <p>
     * 当 Comparator 排序属性值有 null 时
     * 1.必须先把 null 置为 nullsFirst 或 nullsLast 否则会报空指: Comparator.comparing(T:排序属性, Comparator.nullsLast(排序属性类型:compare)
     * 或 Comparator.comparing(T::排序属性，Comparator.nullsFirst(排序属性类型::compare))
     * 2.倒字时,如果依旧想把 null 放在最后，则需先把 null 为 nullsFirst: Comparator.comparing(T::排序属性，Comparator.nullsFirst(排属性类型:compare).reversed()
     */
    public static <T, R> List<R> toDistinctList(List<T> data, Comparator<T> comparator, Function<T, R> function) {
        if (CollectionUtils.isEmpty(data)) {
            return new ArrayList<>();
        }
        return getStream(data, null, comparator, function).distinct().collect(Collectors.toList());
    }

    /**
     * 过游排序后映射某个属性转换去重 List
     * <p>
     * 当 Comparator 排序属性值有 null 时
     * 1.必须先把 null 置为 nullsFirst 或 nullsLast 否则会报空指: Comparator.comparing(T:排序属性, Comparator.nullsLast(排序属性类型:compare)
     * 或 Comparator.comparing(T::排序属性，Comparator.nullsFirst(排序属性类型::compare))
     * 2.倒字时,如果依旧想把 null 放在最后，则需先把 null 为 nullsFirst: Comparator.comparing(T::排序属性，Comparator.nullsFirst(排属性类型:compare).reversed()
     */
    public static <T, R> List<R> toDistinctList(List<T> data, Predicate<T> predicate, Comparator<T> comparator, Function<T, R> function) {
        if (CollectionUtils.isEmpty(data)) {
            return new ArrayList<>();
        }
        return getStream(data, predicate, comparator, function).distinct().collect(Collectors.toList());
    }

    /**
     * 集合过滤
     */
    public static <T> List<T> listFilter(List<T> data, Predicate<T> predicate) {
        if (CollectionUtils.isEmpty(data)) {
            return data;
        }
        return data.stream().filter(predicate).collect(Collectors.toList());
    }

    /**
     * List 转换 Map
     */
    public static <T, U> Map<U, T> listToMap(List<T> data, Function<T, U> key) {
        if (CollectionUtils.isEmpty(data)) {
            return new HashMap<>();
        }
        return data.stream().collect(Collectors.toMap(key, Function.identity(), (oldValue, newValue) -> oldValue, HashMap::new));
    }

    /**
     * List 映射某个属性转换 Map
     */
    public static <T, K, U> Map<K, U> listToMap(List<T> data, Function<T, K> key, Function<T, U> value) {
        if (CollectionUtils.isEmpty(data)) {
            return new HashMap<>();
        }
        return data.stream().collect(Collectors.toMap(key, value, (oldValue, newValue) -> oldValue, HashMap::new));
    }

    /**
     * List 映射某个属性转换 Map，并自定义 merge 方式
     */
    public static <T, K, U> Map<K, U> listToMap(List<T> data, Function<T, K> key, Function<T, U> value, BinaryOperator<U> mergeFunction) {
        if (CollectionUtils.isEmpty(data)) {
            return new HashMap<>();
        }
        return data.stream().collect(Collectors.toMap(key, value, mergeFunction, HashMap::new));
    }

    /**
     * List 转换 Map<Key，List>
     * <p>
     * 当 Function K 性值有 nuLL 时会报空指针
     */
    public static <T, K> Map<K, List<T>> listGroupBy(Collection<T> data, Function<T, K> key) {
        if (CollectionUtils.isEmpty(data)) {
            return new HashMap<>();
        }
        return data.stream().collect(Collectors.groupingBy(key));
    }

    public static <T, K, U> Map<K, List<U>> listGroupBy(List<T> data, Function<T, K> key, Function<T, U> mapper) {
        if (CollectionUtils.isEmpty(data)) {
            return new HashMap<>();
        }
        return data.stream().collect(Collectors.groupingBy(key, HashMap::new, Collectors.mapping(mapper, Collectors.toList())));
    }

    public static <T, K, U> Map<K, Set<U>> setGroupBy(List<T> data, Function<T, K> key, Function<T, U> mapper) {
        if (CollectionUtils.isEmpty(data)) {
            return new HashMap<>();
        }
        return data.stream().collect(Collectors.groupingBy(key, HashMap::new, Collectors.mapping(mapper, Collectors.toSet())));
    }

    /**
     * List 转换 Map<Key，List>
     * <p>
     * 当 Function K 性值有 nuLL 时会报空指针
     */
    public static <T, K> Map<K, List<T>> listGroupByOrderly(List<T> data, Function<T, K> key) {
        if (CollectionUtils.isEmpty(data)) {
            return new HashMap<>();
        }
        return data.stream().collect(Collectors.groupingBy(key, LinkedHashMap::new, Collectors.toList()));
    }

    /**
     * List 转换为字符串 delimiter 拼接
     */
    public static <T> String joining(Collection<T> data, String delimiter) {
        return CollectionUtils.isEmpty(data) ? "" : StringUtils.join(data, delimiter);
    }

    /**
     * 集合根据属性去重
     */
    public static <T> List<T> listDistinctByKey(List<T> data, ToLongFunction<? super T> keyExtractor) {
        if (CollectionUtils.isEmpty(data)) {
            return data;
        }
        return data.stream().collect(collectingAndThen(toCollection(() -> new TreeSet<>(comparingLong(keyExtractor))), ArrayList::new));
    }

}